/*
 * Copyright (C) 2018 Logos Technology Ltd.
 */

package com.logos.tool.image;

import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.media.ExifInterface;
import android.net.Uri;
import android.os.Environment;
import android.provider.MediaStore;
import android.text.Layout;
import android.text.StaticLayout;
import android.text.TextPaint;
import android.text.TextUtils;
import android.util.Log;

import com.logos.tool.AppConstants;
import com.logos.tool.log.LogUtil;
import com.logos.tool.DensityUtil;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.Map;

public class ImageUtils {

    private static final String TAG = ImageUtils.class.getSimpleName();

    /**
     * Scale image size (width and height in pixels) to expected values as close as possible.
     * First, reduce the images sample size to avoid OutOfMemory error;
     * Then scale the image to requested smaller size.
     * Note requestHeight MUST be larger than or equal to requestWidth.
     *
     * @param imagePath     path
     * @param requestWidth  w
     * @param requestHeight h
     * @return bitmap
     */
    public static Bitmap decodeBitmapFromFile(String imagePath, int requestWidth,
                                              int requestHeight) {
        if (TextUtils.isEmpty(imagePath) || requestWidth <= 0 || requestHeight <= 0) {
            LogUtil.warn(TAG, "decodeBitmapFromFile invalid parameters: imagePath "
                    + imagePath + ", requestWidth " + requestWidth + ", requestHeight "
                    + requestHeight);
            return null;
        }

        int reqW = requestWidth;
        int reqH = requestHeight;
        if (reqW > reqH) {
            int tmp = reqW;
            reqW = reqH;
            reqH = tmp;

        }

        BitmapFactory.Options opts = new BitmapFactory.Options();
        opts.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(imagePath, opts); // may produce OutOfMemoryError
        int dstW = opts.outWidth;
        int dstH = opts.outHeight;
        if (dstW > dstH) {
            int tmp = dstW;
            dstW = dstH;
            dstH = tmp;
        }
        opts.inJustDecodeBounds = false;
        opts.inPreferredConfig = Bitmap.Config.RGB_565;

        if (dstW <= reqW && dstH <= reqH) {
            return BitmapFactory.decodeFile(imagePath, opts); // may produce OutOfMemoryError
        }

        /*
         * Sample size is always power of 2.
         * Reduce sample size until both width and height are smaller the requested, then adopt a
         * proper sample size under which width and height are closer to requested.
         * For example:
         *   original w->100, h->400, requested w->30, h->110, then sample size is 4;
         *   original w->100, h->400, requested w->45, h->190, then sample size is 2;
         */
        int sampleSize = 1;
        while ((dstW / sampleSize > reqW) || (dstH / sampleSize > reqH)) {
            sampleSize *= 2;
        }
        if (((reqW - (dstW / sampleSize)) >= ((dstW * 2 / sampleSize) - reqW))
                || ((reqH - (dstH / sampleSize)) >= ((dstH * 2 / sampleSize) - reqH))) {
            sampleSize /= 2;
        }
        opts.inSampleSize = sampleSize;

        // decode image and avoid OutOfMemoryError
        Bitmap bitmap = null;
        do {
            try {
                bitmap = BitmapFactory.decodeFile(imagePath, opts);
            } catch (OutOfMemoryError e) {
                e.printStackTrace();
                opts.inSampleSize *= 2;
                LogUtil.verbose(TAG, "decodeBitmapFromFile OOM, increase inSampleSize to "
                        + opts.inSampleSize);
            }
        } while (null == bitmap && (dstW / opts.inSampleSize) > 0);

        // scale image only if no OutOfMemoryError occurs.
        try {
            bitmap = getSmallerBitmap(bitmap, reqW, reqH);
        } catch (OutOfMemoryError e) {
            e.printStackTrace();
            LogUtil.verbose(TAG, "decodeBitmapFromFile scale img fail for OOM");
        }
        return bitmap;
    }

    /**
     * Note requestHeight MUST be larger than or equal to requestWidth.
     *
     * @param bitmap        bitmap
     * @param requestWidth  w
     * @param requestHeight h
     * @return bitmap
     */
    public static Bitmap getSmallerBitmap(Bitmap bitmap, int requestWidth, int requestHeight) {
        if (null == bitmap || requestWidth <= 0 || requestHeight <= 0) {
            return bitmap;
        }

        int dstW = requestWidth;
        int dstH = requestHeight;
        if (dstW > dstH) {
            int tmp = dstW;
            dstW = dstH;
            dstH = tmp;
        }

        int srcW = bitmap.getWidth();
        int srcH = bitmap.getHeight();
        if (srcW > srcH) {
            int tmp = srcW;
            srcW = srcH;
            srcH = tmp;
        }

        if (srcW <= dstW && srcH <= dstH) {
            return bitmap;
        }

        float sx = dstW / (float) srcW;
        float sy = dstH / (float) srcH;
        float ratio = (sx < sy) ? sx : sy;
        Matrix matrix = new Matrix();
        matrix.postScale(ratio, ratio);

        return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(),
                matrix, true);
    }

    /**
     * Compress the size of jpg image by decreasing its quality and then store it in file.
     *
     * @param image   Bitmap to be compressed.
     * @param reqSize Max expected size of the compressed image, in KBytes.
     *                Note the actual compressed image may be larger than "reqSize"
     *                in case of too low quantity.
     * @param path    File path to save the compressed image.
     * @throws IOException if saving the image to file fail.
     */
    public static void compressImageAndSaveAsJpg(Bitmap image, int reqSize, String path)
            throws IOException {
        if (null == image || null == path) {
            LogUtil.warn(TAG, "compressImageAndSaveAsJpg invalid parameters: image "
                    + image + ", path " + path);
            return;
        }

        final int maxsizeinbytes = reqSize * 1024;
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        int options = 105;
        do {
            baos.reset();
            options -= 5;
            image.compress(Bitmap.CompressFormat.JPEG, options, baos);
        } while (baos.toByteArray().length > maxsizeinbytes && options > 50);

        FileOutputStream fos = new FileOutputStream(new File(path));
        fos.write(baos.toByteArray());
        fos.flush();
        fos.close();
    }

    /**
     * 从缩率图地址中获取原始图片地址.
     *
     * @param thumbnailUrl 缩率图地址Url
     * @return 原始图片地址Url
     */
    public static String getOriginalImgFromThumbnailUrl(String thumbnailUrl) {
        if (null == thumbnailUrl) {
            return null;
        }
        int index = thumbnailUrl.lastIndexOf('@');
        if (index <= 0) {
            return thumbnailUrl;
        }
        String originalUrl = thumbnailUrl.replace(".img-", ".oss-").substring(0, index);
        Log.d(TAG, "getOriginalImgFromThumbnailUrl: OriginalUrl" + originalUrl);
        return originalUrl;
    }

    /**
     * 读取图片的旋转的角度.
     *
     * @param path 图片绝对路径
     * @return 图片的旋转角度
     */
    public static int getBitmapDegree(String path) {
        int degree = 0;
        try {
            // 从指定路径下读取图片，并获取其EXIF信息
            ExifInterface exifInterface = new ExifInterface(path);
            // 获取图片的旋转信息
            int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION,
                    ExifInterface.ORIENTATION_NORMAL);
            switch (orientation) {
                case ExifInterface.ORIENTATION_ROTATE_90:
                    degree = 90;
                    break;
                case ExifInterface.ORIENTATION_ROTATE_180:
                    degree = 180;
                    break;
                case ExifInterface.ORIENTATION_ROTATE_270:
                    degree = 270;
                    break;
                default:
                    break;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return degree;
    }

    /**
     * 将图片按照某个角度进行旋转.
     *
     * @param bm     需要旋转的图片
     * @param degree 旋转角度
     * @return 旋转后的图片
     */
    public static Bitmap rotateBitmapByDegree(Bitmap bm, int degree) {
        Bitmap returnBm = null;

        if (degree == 0) {
            return bm;
        }
        // 根据旋转角度，生成旋转矩阵
        Matrix matrix = new Matrix();
        matrix.postRotate(degree);
        try {
            // 将原始图片按照旋转矩阵进行旋转，并得到新的图片
            returnBm = Bitmap.createBitmap(bm, 0, 0, bm.getWidth(), bm.getHeight(), matrix, true);
        } catch (OutOfMemoryError e) {
            LogUtil.debug(TAG, e.getMessage());
        }
        if (returnBm == null) {
            returnBm = bm;
        }
        return returnBm;
    }

    /**
     * 往图片中添加额外信息.
     *
     * @param picOriginPath           图片路径.
     * @param extraPictureInformation 额外信息.
     * @return 图片路径
     */
    public static String writeExtraPicInfo(String picOriginPath,
                                           Map<String, String> extraPictureInformation) {
        if (extraPictureInformation == null || extraPictureInformation.isEmpty()) {
            return picOriginPath;
        }
        try {
            // 获取图片Exif
            ExifInterface exif = new ExifInterface(picOriginPath);
            for (Map.Entry<String, String> entry : extraPictureInformation.entrySet()) {
                exif.setAttribute(entry.getKey(), entry.getValue());
            }
            exif.saveAttributes();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return picOriginPath;
    }

    /**
     * 获取图片信息.
     *
     * @param picPath 图片路径
     * @param key     图片信息key
     * @return 图片信息
     */
    public static String getPicInfo(String picPath, String key) {
        try {
            ExifInterface exif = new ExifInterface(picPath);
            return exif.getAttribute(key);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return "";
    }

    /**
     * 复制旧图片信息到新图片.
     *
     * @param oldFilePath 旧图片路径
     * @param newFilePath 新图片路径
     * @throws IOException            Exception
     * @throws IllegalAccessException Exception
     */
    public static void saveExif(String oldFilePath, String newFilePath)
            throws IOException, IllegalAccessException {

        ExifInterface oldExif = new ExifInterface(oldFilePath);
        ExifInterface newExif = new ExifInterface(newFilePath);
        Class<ExifInterface> cls = ExifInterface.class;
        Field[] fields = cls.getFields();
        for (int i = 0; i < fields.length; i++) {
            String fieldName = fields[i].getName();
            if (!TextUtils.isEmpty(fieldName) && fieldName.startsWith("TAG")) {
                String fieldValue = fields[i].get(cls).toString();
                String attribute = oldExif.getAttribute(fieldValue);
                if (attribute != null) {
                    newExif.setAttribute(fieldValue, attribute);
                }
            }
        }
        newExif.saveAttributes();
    }

    /**
     * 设置水印图片在左上角.
     *
     * @param src         原图片
     * @param watermark   水印图片
     * @param paddingLeft 左边距
     * @param paddingTop  上边距
     * @return 水印图片
     */
    public static Bitmap createWaterMaskLeftTop(
            Bitmap src, Bitmap watermark,
            int paddingLeft, int paddingTop) {
        return createWaterMaskBitmap(src, watermark,
                DensityUtil.dip2px(paddingLeft), DensityUtil.dip2px(paddingTop));
    }

    private static Bitmap createWaterMaskBitmap(Bitmap src, Bitmap watermark,
                                                int paddingLeft, int paddingTop) {
        if (src == null) {
            return null;
        }
        int width = src.getWidth();
        int height = src.getHeight();
        //创建一个bitmap
        Bitmap newb = Bitmap.createBitmap(width, height,
                Bitmap.Config.ARGB_8888);// 创建一个新的和SRC长度宽度一样的位图
        //将该图片作为画布
        Canvas canvas = new Canvas(newb);
        //在画布 0，0坐标上开始绘制原始图片
        canvas.drawBitmap(src, 0, 0, null);
        //在画布上绘制水印图片
        canvas.drawBitmap(watermark, paddingLeft, paddingTop, null);
        // 保存
        canvas.save();
        // 存储
        canvas.restore();
        return newb;
    }

    /**
     * 设置水印图片在右下角.
     *
     * @param src           原图
     * @param watermark     水印图片
     * @param paddingRight  右边距
     * @param paddingBottom 底部边距
     * @return 水印图片
     */
    public static Bitmap createWaterMaskRightBottom(
            Bitmap src, Bitmap watermark,
            int paddingRight, int paddingBottom) {
        return createWaterMaskBitmap(src, watermark,
                src.getWidth() - watermark.getWidth() - DensityUtil.dip2px(paddingRight),
                src.getHeight() - watermark.getHeight() - DensityUtil.dip2px(paddingBottom));
    }

    /**
     * 设置水印图片到右上角.
     *
     * @param src          原始图片
     * @param watermark    水印图片
     * @param paddingRight 右边距
     * @param paddingTop   上边距
     * @return 水印图片
     */
    public static Bitmap createWaterMaskRightTop(
            Bitmap src, Bitmap watermark,
            int paddingRight, int paddingTop) {
        return createWaterMaskBitmap(src, watermark,
                src.getWidth() - watermark.getWidth() - DensityUtil.dip2px(paddingRight),
                DensityUtil.dip2px(paddingTop));
    }

    /**
     * 设置水印图片到左下角.
     *
     * @param src           原始图片
     * @param watermark     水印图片
     * @param paddingLeft   左边距
     * @param paddingBottom 底部边距
     * @return 水印图片
     */
    public static Bitmap createWaterMaskLeftBottom(
            Bitmap src, Bitmap watermark,
            int paddingLeft, int paddingBottom) {
        return createWaterMaskBitmap(src, watermark, DensityUtil.dip2px(paddingLeft),
                src.getHeight() - watermark.getHeight() - DensityUtil.dip2px(paddingBottom));
    }

    /**
     * 设置水印图片到中间.
     *
     * @param src       原始图片
     * @param watermark 水印图片
     * @return 水印图片
     */
    public static Bitmap createWaterMaskCenter(Bitmap src, Bitmap watermark) {
        return createWaterMaskBitmap(src, watermark,
                (src.getWidth() - watermark.getWidth()) / 2,
                (src.getHeight() - watermark.getHeight()) / 2);
    }

    /**
     * 给图片添加文字到左上角
     *
     * @param bitmap      bitmap
     * @param text        text
     * @param topPadding  top
     * @param leftPadding left
     * @param size        size
     * @param color       color
     * @return bitmap
     */
    public static Bitmap drawTextToLeftTop(Bitmap bitmap, String text, float topPadding,
                                           float leftPadding, int size, int color) {
        TextPaint paint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
        paint.setColor(color);
        paint.setTextSize(DensityUtil.dip2px(size));
        Rect bounds = new Rect();
        paint.getTextBounds(text, 0, text.length(), bounds);
        return drawTextToBitmap(bitmap, text, paint, topPadding, leftPadding);
    }

    //图片上绘制文字
    private static Bitmap drawTextToBitmap(Bitmap bitmap, String text,
                                           TextPaint paint, float topPadding,
                                           float leftPadding) {
        Bitmap.Config bitmapConfig = bitmap.getConfig();

        paint.setDither(true); // 获取跟清晰的图像采样
        paint.setFilterBitmap(true);// 过滤一些
        if (bitmapConfig == null) {
            bitmapConfig = Bitmap.Config.ARGB_8888;
        }
        bitmap = bitmap.copy(bitmapConfig, true);
        Canvas canvas = new Canvas(bitmap);
        canvas.translate(leftPadding, topPadding);
        StaticLayout layout = new StaticLayout(text, paint, 1280,
                Layout.Alignment.ALIGN_NORMAL, 1.0F, 0.0F, true);
        layout.draw(canvas);
        return bitmap;
    }

    /**
     * 缩放图片
     *
     * @param src       image
     * @param srcWidth  w
     * @param srcHeight h
     * @return Bitmap
     */
    public static Bitmap scaleWithWh(Bitmap src, double srcWidth, double srcHeight) {
        if (srcWidth == 0 || srcHeight == 0 || src == null) {
            return src;
        } else {
            // 记录src的宽高
            int width = src.getWidth();
            int height = src.getHeight();
            // 创建一个matrix容器
            Matrix matrix = new Matrix();
            // 计算缩放比例
            float scaleWidth = (float) (srcWidth / width);
            float scaleHeight = (float) (srcHeight / height);
            // 开始缩放
            matrix.postScale(scaleWidth, scaleHeight);
            // 创建缩放后的图片
            return Bitmap.createBitmap(src, 0, 0, width, height, matrix, true);
        }
    }

    /**
     * drawable to bitmap
     *
     * @param drawable Drawable
     * @return return bitmap
     */
    public static Bitmap drawableToBitmap(Drawable drawable) {
        if (drawable == null) {
            return null;
        } else if (drawable instanceof BitmapDrawable) {
            return ((BitmapDrawable) drawable).getBitmap();
        }

        int intrinsicWidth = drawable.getIntrinsicWidth();
        int intrinsicHeight = drawable.getIntrinsicHeight();

        if (!(intrinsicWidth > 0 && intrinsicHeight > 0)) {
            return null;
        }
        try {
            Bitmap.Config config = drawable.getOpacity() != PixelFormat.OPAQUE
                    ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565;
            Bitmap bitmap = Bitmap.createBitmap(intrinsicWidth, intrinsicHeight, config);
            Canvas canvas = new Canvas(bitmap);
            drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
            drawable.draw(canvas);
            return bitmap;
        } catch (OutOfMemoryError e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 保存图片到本地
     *
     * @param context 上下文
     * @param bmp     Bitmap实例.
     */
    public static void saveImageToGallery(Context context, Bitmap bmp) {
        if (bmp == null) {
            return;
        }

        File appDir = new File(Environment.getExternalStorageDirectory(),
                AppConstants.FILE_ROOT_NAME);
        if (!appDir.exists()) {
            appDir.mkdir();
        }
        String fileName = System.currentTimeMillis() + ".jpg";
        File file = new File(appDir, fileName);
        try {
            FileOutputStream fos = new FileOutputStream(file);
            bmp.compress(Bitmap.CompressFormat.JPEG, 100, fos);
            fos.flush();
            fos.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        try {
            MediaStore.Images.Media.insertImage(context.getContentResolver(),
                    file.getAbsolutePath(), fileName, null);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE,
                Uri.fromFile(appDir)));
    }
}
